package com.zzyl.nursing.service.impl;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.SecurityUtils;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.common.utils.uuid.UUID;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.ProductVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.service.IDeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import static javax.management.Query.eq;

/**
 * deviceService业务层处理
 * 
 * @author alexis
 * @date 2025-11-17
 */
@Slf4j
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private IoTDAClient client;


    /**
     * 查询device
     * 
     * @param id device主键
     * @return device
     */
    @Override
    public Device selectDeviceById(Long id)
    {
        return deviceMapper.selectById(id);
    }

    /**
     * 查询device列表
     * 
     * @param device device
     * @return device
     */
    @Override
    public List<Device> selectDeviceList(Device device)
    {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增device
     * 
     * @param device device
     * @return 结果
     */
    @Override
    public int insertDevice(Device device)
    {
        return deviceMapper.insert(device);
    }

    /**
     * 修改device
     * 
     * @param device device
     * @return 结果
     */
    @Override
    public int updateDevice(Device device)
    {
        return deviceMapper.updateById(device);
    }

    /**
     * 批量删除device
     * 
     * @param ids 需要删除的device主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids)
    {
        return deviceMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除device信息
     * 
     * @param id device主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id)
    {
        return deviceMapper.deleteById(id);
    }
    /**
     * 同步产品列表
     */
    @Override
    public void syncProductList() {
        ListProductsRequest listProductsRequest = new ListProductsRequest();
        listProductsRequest.setLimit(50);
        ListProductsResponse listProductsResponse = client.listProducts(listProductsRequest);
        if(listProductsResponse.getHttpStatusCode()!=200){
            throw new BaseException("物联网接口 - 查询产品，同步失败");
        }
        redisTemplate.opsForValue().set(CacheConstants.ALL_PRODUCT_KEY, JSONUtil.toJsonStr(listProductsResponse.getProducts()));
    }

    /**
     * 查询所有产品列表
     *
     * @return 产品列表
     */
    @Override
    public List<ProductVo> allProduct() {
        String jsonStr =  redisTemplate.opsForValue().get(CacheConstants.ALL_PRODUCT_KEY);
        if (StringUtils.isEmpty( jsonStr)){
            return Collections.emptyList();
        }
        return JSONUtil.toList(jsonStr, ProductVo.class);
    }

    /**
     * 注册设备
     *
     * @param deviceDto
     */
    @Override
    public void registerDevice(DeviceDto deviceDto) {
        //1. 判断设备名称是否重复
        Long x = lambdaQuery().
                eq(Device::getDeviceName,deviceDto.getDeviceName())
                .count();
        if(x>0){
            throw new BaseException("设备名称重复");
        }
        //2. 判断设备标识是否重复
        x=lambdaQuery().
                eq(Device::getNodeId,deviceDto.getNodeId())
                .count();
        if(x>0){
            throw new BaseException("设备标识码重复");
        }
        //判断如果是随身设备，将物理位置设置为-1
        if(deviceDto.getLocationType() == 0 ){
            deviceDto.setPhysicalLocationType(-1);
        }
        //3. 判断同一位置是否绑定了相同的产品
        x=lambdaQuery()
                .eq(Device::getBindingLocation,deviceDto.getBindingLocation())
                .eq(Device::getProductKey,deviceDto.getProductKey())
                .eq(Device::getLocationType,deviceDto.getLocationType())
                .eq(Device::getPhysicalLocationType,deviceDto.getPhysicalLocationType())
                .count();
        if(x>0){
            throw new BaseException("产品在一个位置重复");
        }
        //4. 注册设备到华为iot平台
        String secret = UUID.randomUUID().toString().replaceAll("-","");

        AddDeviceRequest addDeviceRequest = new AddDeviceRequest();
        AddDevice addDevice = new AddDevice();
        addDevice
                .withDeviceName(deviceDto.getDeviceName())
                .withProductId(deviceDto.getProductKey())
                .withNodeId(deviceDto.getNodeId())
                .withAuthInfo(new AuthInfo().withSecret(secret));

        addDeviceRequest.withBody(addDevice);
        AddDeviceResponse addDeviceResponse = null;
        try {
            addDeviceResponse = client.addDevice(addDeviceRequest);
        } catch (Exception e) {
            throw new BaseException("物联网接口 - 注册设备失败");
        }
        //5.保存设备数据到数据库
        Device device = new Device();
        BeanUtils.copyProperties(deviceDto,device);
        device.setIotId(addDeviceResponse.getDeviceId());
        device.setSecret(secret);
        save(device);
    }

    /**
     * 查询设备详情
     *
     * @param iotId
     * @return
     */
    @Override
    public DeviceDetailVo queryDeviceDetail(String iotId) {
        //1.查询数据库
//        构建一个查询条件：查询 Device 表中字段 iot_id 值等于传入的 iotId 的记录。
        Device device = getOne(Wrappers.<Device>lambdaQuery().eq(Device::getIotId,iotId));
        if(ObjectUtil.isEmpty(device)){
            return null;
        }
        //2.调用华为云物联网接口,华为云 IoTDA SDK 中专门用于 查询设备详情 的请求对象。
//        当你想通过 SDK 调用华为云 IoT 平台的 API 去查某个设备的信息，就需要：
        ShowDeviceRequest request = new ShowDeviceRequest();
        //设置请求参数
        request.setDeviceId(iotId);
        //client 里已经包含了：华为云 AK/SK/endpoint（区域）/projectId/IoTDA 的认证信息/token 等必要数据
        ShowDeviceResponse response;
        //try-catch 调用云端接口
        try {
           response =  client.showDevice( request) ;

        }catch (Exception e){
            log.info("物联网接口 - 查询设备详情失败",e.getMessage());
            throw new BaseException("物联网接口 - 查询设备详情失败");
        }
        //3.属性拷贝
//        本地数据 + 云端数据 = 前端需要的完整设备详情
//        3.1. 对象复制：把 Device 转成 DeviceDetailVo
         DeviceDetailVo deviceDetailVo = BeanUtil.toBean(device, DeviceDetailVo.class);
//         3.2取出云端返回的激活时间  例如：2024-03-21T09:45:30.123Z
        String activeTimeStr = response.getActiveTime();
//        3.3 判断激活时间是否为空
        if(StringUtils.isNotEmpty(activeTimeStr)){
            LocalDateTime activeTime =LocalDateTimeUtil.parse(activeTimeStr, DatePattern.UTC_MS_PATTERN);
//            核心学习：日期时区转换
            activeTime=activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                    .withZoneSameInstant(ZoneId.of("Asia/shanghai"))
                    .toLocalDateTime();
            deviceDetailVo.setActiveTime(activeTime);

        }
        return deviceDetailVo;
    }
    /**
     * 查询设备上报数据
     *
     * @param iotId
     * @return
     */
    @Override
    public AjaxResult queryServiceProperties(String iotId) {
//        请求对象模型类（Request Model）
        ShowDeviceShadowRequest showDeviceShadowRequest = new  ShowDeviceShadowRequest();
//        往请求对象里设置设备 ID — 必填参数
            showDeviceShadowRequest.setDeviceId(iotId);
//        响应类（Response Model）—— 存放华为云返回的设备影子数据
//        showDeviceShadow() 方法用于发送“查询设备影子”的 API 请求
//        ShowDeviceShadowResponse —— 类（返回的数据模型）
        ShowDeviceShadowResponse showDeviceShadowResponse=client.showDeviceShadow(showDeviceShadowRequest);
        if(showDeviceShadowResponse.getHttpStatusCode()!=200){
            throw new BaseException("物联网接口 - 查询设备影子失败");
        }
//        DeviceShadowData:用于表示设备影子（Device Shadow）的数据结构。
        List<DeviceShadowData> shadow =showDeviceShadowResponse.getShadow();
        //在设备影子数据为空时，返回一个“成功”状态的空列表给前端（或调用方）
        if(CollUtil.isEmpty( shadow)){
            List<Object> emptylist = Collections.emptyList();
            return AjaxResult.success(emptylist);
        }
//        shadow.get(0)这个设备的第一个影子（通常也是唯一的影子）一个设备可能有多个“影子服务”（一般只有一个）
        //shadow 是“影子列表（List）”，不是设备列表！
        JSONObject jsonObject = JSONUtil.parseObj(shadow.get(0).getReported().getProperties());
        List<Map<String,Object>>   arrayList = new ArrayList<>();
//          时间处理

        LocalDateTime activeTime =LocalDateTimeUtil.parse(shadow.get(0).getReported().getEventTime(),"yyyyMMdd'T'HHmmss'Z'");
        LocalDateTime eventTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                .withZoneSameInstant(ZoneId.of("Asia/shanghai"))
                .toLocalDateTime();
//        这里遍历的只是 第一个影子的属性 JSON,也就是说，如果设备有第二个影子（shadow.get(1)），这一段代码是忽略掉的
        jsonObject.forEach((key,value)->{
            Map<String,Object> map = new HashMap<>();
            map.put("functionId",key);
            map.put("value",value);
            map.put("eventTime",eventTime);
            arrayList.add(map);
        });
        return AjaxResult.success(arrayList);
    }

    /**
     * 查询产品详情
     * @param productKey
     * @return
     */
    @Override
    public AjaxResult queryProduct(String productKey) {
        if(StringUtils.isEmpty(productKey)){
            throw new BaseException("产品ID不能为空");
        }
        ////调用华为云物联网接口
        ShowProductRequest request = new ShowProductRequest();
        request.setProductId(productKey);
        //声明变量
        ShowProductResponse response;
        try {
            response = client.showProduct(request);
        }catch (Exception e){
            throw new BaseException("物联网接口 - 查询产品详情失败");
        }
        List<ServiceCapability> serviceCapabilities = response.getServiceCapabilities();
        if(CollUtil.isEmpty(serviceCapabilities)){
            return AjaxResult.success(Collections.emptyList());
        }
        return AjaxResult.success(serviceCapabilities);
    }
}
